﻿using System;
using System.Collections.Generic;
using System.Linq;
using Abp.Auditing;
using Abp.Dependency;
using Abp.Domain.Entities;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using Abp.Extensions;
using Castle.Core.Logging;
using DHJ.Cost.CostBooking;
using DHJ.Cost.CostBooking.Config;
using DHJ.Cost.ManHours;
using DHJ.Cost.Quality;
using DHJ.Cost.RealTimeCost;
using DHJ.Cost.Tasks;
using DHJ.Cost.Timing;
using DHJ.Cost.Workshops;
using DHJ.Messages.Cost;
using DHJ.Messages.Cost.RealTime;
using DHJ.Messages.Production;
using DHJ.Messages.Quality;
using Microsoft.EntityFrameworkCore;
using Task = System.Threading.Tasks.Task;

namespace DHJ.Cost.MessageHandle
{
    [DisableAuditing]
    public class MessageHandleAppService : CostAppServiceBase
    {
        private readonly ILogger _logger;
        private readonly IRepository<EstimatedCost, Guid> _estimatedCostRepository;
        private readonly IEstimatedCostManager _estimatedCostManager;
        private readonly CostCategoryConfig _configuration;

        private readonly IRepository<Tasks.Task, long> _taskRepository;
        private readonly IRepository<Batch, string> _batchRepository;

        private readonly IRepository<AssemRel> _assemRelRepository;
        private readonly IRepository<QcCard, string> _qcCardRepository;
        private readonly IRepository<KitItem, string> _kitItemRepository;
        private readonly IRepository<BatchManHour> _batchManHourRepository;
        private readonly IRepository<DirectCost, Guid> _costRepository;

        public MessageHandleAppService(ILogger logger, IRepository<EstimatedCost, Guid> estimatedCostRepository, IEstimatedCostManager estimatedCostManager, CostCategoryConfig configuration, IRepository<Tasks.Task, long> taskRepository, IRepository<Batch, string> batchRepository, IRepository<AssemRel> assemRelRepository)
        {
            _logger = logger;
            _estimatedCostRepository = estimatedCostRepository;
            _estimatedCostManager = estimatedCostManager;
            _configuration = configuration;
            _taskRepository = taskRepository;
            _batchRepository = batchRepository;
            _assemRelRepository = assemRelRepository;
        }



        public async Task HandleBarcodeCreated(BarcodeCreated message)
        {
            try
            {

                var task = await _taskRepository.FirstOrDefaultAsync(e => e.SysId == message.TaskId);

                if (task == null)
                {
                    _logger.Error("没有找到 SysId 为 " + message.TaskId + "的任务信息");
                }
                else
                {
                    var batch = new Batch(message.FullBarcode, task.Id, message.InitialQuantity, message.QcCard,
                        message.ParentBarcode, message.ParentQuantity);
                    await _batchRepository.InsertAsync(batch);
                }
            }
            catch (Exception e)
            {
                _logger.Error($"Message: {e.Message} | StackTrace: {e.StackTrace}");
            }
        }

        public async Task HandleBarcodeDeleted(BarcodeDeleted message)
        {
            try
            {

                await _batchRepository.DeleteAsync(message.Barcode);
            }
            catch (Exception e)
            {
                _logger.Error($"Message: {e.Message} | StackTrace: {e.StackTrace}");
            }
        }

        public async Task HandleTaskAccepted(TaskAccepted message)
        {
            try
            {

                var task = await _taskRepository
                    .FirstOrDefaultAsync(e => e.TaskNum == message.TaskNum && e.ProductId == message.ProductId);

                if (message.TaskAcceptedType == TaskAcceptedType.删除)
                {
                    if (task != null)
                        await _taskRepository.DeleteAsync(task);
                }
                else
                {
                    var modelId = message.ModelId.IsNullOrEmpty() ? "-" : message.ModelId;

                    if (task != null)
                    {
                        task.SysId = message.TaskId.ToString();
                        task.ModelId = modelId;
                        task.ProductName = message.ProductName;
                        task.PlannedQuantity = message.PlannedQuantity;
                        task.ProductionQuantity = message.ProductionQuantity;

                        await _taskRepository.UpdateAsync(task);
                    }
                    else
                    {
                        task = new Tasks.Task(message.TaskId.ToString(), message.TaskNum, modelId,
                            message.ProductName, message.ProductId,
                            message.PlannedQuantity,
                            message.ProductionQuantity);

                        await _taskRepository.InsertAsync(task);
                    }
                }
            }
            catch (Exception e)
            {
                _logger.Error($"Message: {e.Message} | StackTrace: {e.StackTrace}");
            }
        }

        public async Task ActualAssemble(KitItemStockOut message)
        {
            try
            {
                if (message.ItemSort != ItemSort.零件) return;

                var assembly = await _batchRepository.FirstOrDefaultAsync(b => b.QcCard == message.CardCode);
                var part = await _batchRepository
                    .FirstOrDefaultAsync(b => b.Certificate == message.Certificate);
                if (assembly == null || part == null) return;


                _logger.Info(
                    $"Received KitItemStockOut, QcCard = {message.CardCode}, Certificate = {message.Certificate}, Quantity = {message.Quantity}");

                var assemRel = await _assemRelRepository
                    .FirstOrDefaultAsync(a => a.AssemId == assembly.Id && a.PartId == part.Id);
                if (assemRel == null)
                {
                    assemRel = new AssemRel
                    {
                        AssemId = assembly.Id,
                        PartId = part.Id,
                        PartQuantity = message.Quantity
                    };
                    await _assemRelRepository.InsertAsync(assemRel);
                }
                else
                {
                    assemRel.PartQuantity += message.Quantity;
                    await _assemRelRepository.UpdateAsync(assemRel);
                }

                part.StockOut(message.Quantity);
            }
            catch (Exception e)
            {
                _logger.Error($"Message: {e.Message} | StackTrace: {e.StackTrace}");
            }
        }

        public async Task HandleTransferListCreated(TransferListCreated message)
        {
            try
            {
                _logger.Info($"Received TransferListCreated, TransferListId = {message.TransferListId}");

                foreach (var barcode in message.Barcodes)
                {
                    var batch = await _batchRepository.GetAsync(barcode.Barcode);
                    batch.CompleteTask(barcode.Certificate, barcode.QualifiedQuantity);
                }
            }
            catch (Exception e)
            {
                _logger.Error($"Message: {e.Message} | StackTrace: {e.StackTrace}");
            }
        }

        [UnitOfWork]
        public virtual async Task HandleCostEstimated(CostEstimated message)
        {
            var cost = new EstimatedCost()
            {
                Workshop = message.Workshop,
                BatchId = message.BatchId,
                Quantity = message.Quantity,
                ProcessSequence = message.ProcessSequence,
                ProcessName = message.ProcessName,
                Amount = message.Amount,
                Time = message.Time,
                Formula = message.Formula,
                Remark = message.Remark,
                CreationTime = DateTime.Now,
                CreatorUserId = 2
            };

            var s = message.Type.Split('.');

            var categories = _configuration.Categories;
            var category = categories.SingleOrDefault(c => c.Key == s[0]);
            if (category == null) return;

            cost.Category = category.Value;
            if (s.Length > 1)
            {
                cost.Type = category.Types.SingleOrDefault(t => t.Key == s[1])?.Value;
            }

            await _estimatedCostRepository.InsertAsync(cost);

            // 向前端发送消息
            await _estimatedCostManager.SendCostMessageAsync(cost);
        }

        public async Task HandleKitItemInsertedOrUpdated(KitItemInsertedOrUpdated message)
        {
            try
            {
                _logger.Info(
                    $"Received KitItemCreatedOrUpdate, QcCard = {message.CardCode}, Sequence = {message.Sequence}");


                if (!_qcCardRepository.GetAll().Any(qc => qc.Id == message.CardCode))
                {
                    var qcCard = new QcCard { Id = message.CardCode };
                    _qcCardRepository.Insert(qcCard);
                }

                var kitItem = await _kitItemRepository.GetAll()
                    .FirstOrDefaultAsync(e => e.Id == message.ItemDetailSystemId);


                if (kitItem == null)
                {
                    kitItem = new KitItem
                    {
                        Id = message.ItemDetailSystemId,
                        QcCardId = message.CardCode,
                        Sequence = message.Sequence,
                        Certificate = message.Certificate,
                        DrawingNumber = message.DrawingNumber,
                        Name = message.Name,
                        ItemAmount = message.ItemAmount,
                        DetailAmount = message.DetailAmount,
                        ItemSort = message.ItemSort
                    };
                    await _kitItemRepository.InsertAsync(kitItem);
                }
                else
                {
                    kitItem.QcCardId = message.CardCode;
                    kitItem.Sequence = message.Sequence;
                    kitItem.Certificate = message.Certificate;
                    kitItem.DrawingNumber = message.DrawingNumber;
                    kitItem.Name = message.Name;
                    kitItem.ItemAmount = message.ItemAmount;
                    kitItem.DetailAmount = message.DetailAmount;
                    kitItem.ItemSort = message.ItemSort;

                    await _kitItemRepository.UpdateAsync(kitItem);
                }
            }
            catch (Exception e)
            {
                _logger.Error($"Message: {e.Message} | StackTrace: {e.StackTrace}");
            }
        }

        public async Task HandleKitItemDelete(KitItemDeleted message)
        {
            try
            {
                _logger.Info($"Received KitItemDeleted, QcCard = {message.CardCode}, Sequence = {message.Sequence}");

                await _kitItemRepository.DeleteAsync(message.ItemDetailSystemId);
            }
            catch (Exception e)
            {
                _logger.Error($"Message: {e.Message} | StackTrace: {e.StackTrace}");
            }
        }

        public async Task HandleKitItemStockOut(KitItemStockOut message)
        {
            try
            {
                _logger.Info(
                    $"Received KitItemStockOut, QcCard = {message.CardCode}, Sequence = {message.Sequence}, Quantity = {message.Quantity}");

                var kitItem = await _kitItemRepository.GetAsync(message.ItemDetailSystemId);
                kitItem.StockedOutAmount += message.Quantity;
                await _kitItemRepository.UpdateAsync(kitItem);
            }
            catch (Exception e)
            {
                _logger.Error($"Message: {e.Message} | StackTrace: {e.StackTrace}");
            }
        }

        public async Task HandleManHourReported(ManHourReported message)
        {
            try
            {
                _logger.Info("Received ManHourReported.");

                var workshop = (Workshop)Enum.Parse(typeof(Workshop), message.DeptCode);
                var batchManHour = new BatchManHour(new OccurrenceTime(message.Year, message.Month), workshop,
                    message.Barcode, message.Quantity, new ManHour((decimal)message.ManHours));


                await _batchManHourRepository.InsertOrUpdateAsync(batchManHour);
            }
            catch (Exception e)
            {
                _logger.Error($"Message: {e.Message} | StackTrace: {e.StackTrace}");
            }
        }

        public async Task HandleMaterialCost(MaterialCost message)
        {
            try
            {
                _logger.Info("Received MaterialCost.");


                var workshop = (Workshop)Convert.ToInt32(message.Workshop);
                var cost = new DirectCost(
                    new OccurrenceTime(DateTime.Now.Year, DateTime.Now.Month),
                    new CostSubject(message.Workshop),
                    new CostDetail("原材料成本", "原材料成本", "-", message.Amount),
                    workshop,
                    new List<BatchIdentifier>
                    {
                        new BatchIdentifier(message.BatchId, message.Quantity)
                    });

                cost.SetData("原材料成本_Extend", new 原材料成本_Extend
                {
                    出库单号 = message.OutBillId,
                    出库日期 = message.OutDate.ToString("yyyy-MM-dd"),
                    领用单位 = message.UsedDept
                });

                await _costRepository.InsertAsync(cost);
            }
            catch (Exception e)
            {
                _logger.Error($"Message: {e.Message} | StackTrace: {e.StackTrace}");
            }
        }
    }
}